package com.springboot.file.util.easyexcel;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.springboot.file.exception.BusinessException;
import com.springboot.file.util.easyexcel.converter.*;
import com.springboot.file.util.easyexcel.exception.TransactionProcessingException;
import com.springboot.file.util.easyexcel.listener.AbstractBusinessListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.util.Collections;
import java.util.List;

/**
 * Excel事务服务实现类
 * 负责在事务中处理Excel导入，确保所有sheet要么全部成功，要么全部失败
 */
@Service
@Slf4j
public class ExcelTransactionServiceImpl implements ExcelTransactionService {

    /**
     * 在事务中处理多表格Excel导入
     * 通过在独立的服务类中实现，解决Spring事务管理的自调用问题
     *
     * @param fileBytes Excel文件的字节数组
     * @param readSheets 要处理的ReadSheet列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processInTransaction(byte[] fileBytes, List<ReadSheet> readSheets) {
        log.info("开始在事务中处理所有sheet，共{}个sheet", readSheets.size());
        long transactionStartTime = System.currentTimeMillis();
        
        // 创建一个处理上下文对象，用于在监听器之间共享状态
        EasyExcelUtil.TransactionContext transactionContext = new EasyExcelUtil.TransactionContext();
        
        try {
            // 第一阶段：先进行所有sheet的数据校验
            validateAllSheets(fileBytes, readSheets, transactionContext);
            
            // 第二阶段：校验通过后，执行实际的数据保存
            processAllSheets(fileBytes, readSheets, transactionContext);
            
            // 事务处理成功
            log.info("在事务中处理所有sheet完成，耗时: {}ms", System.currentTimeMillis() - transactionStartTime);
        } catch (Exception e) {
            String errorMsg = transactionContext.isFailed() ? 
                transactionContext.getErrorMessage() : e.getMessage();
            log.error("事务处理过程中发生异常，所有数据将回滚: {}", errorMsg, e);
            throw new TransactionProcessingException(errorMsg, e);
        }
    }
    
    /**
     * 验证所有sheet的数据有效性
     * 
     * @param fileBytes 文件字节数组
     * @param readSheets 读取sheet配置
     * @param transactionContext 事务上下文
     * @throws TransactionProcessingException 如果校验失败
     */
    private void validateAllSheets(byte[] fileBytes, List<ReadSheet> readSheets, 
                               EasyExcelUtil.TransactionContext transactionContext) {
        log.info("开始验证全部{}个sheet的数据", readSheets.size());
        
        try (ExcelReader excelReader = createExcelReader(fileBytes)) {
            // 设置监听器为校验模式
            setupValidationMode(readSheets, transactionContext);
            
            // 依次校验每个sheet
            validateEachSheet(excelReader, readSheets, transactionContext);
            
            log.info("所有sheet数据校验通过");
        } catch (TransactionProcessingException | BusinessException e) {
            // 直接向上传递自定义异常，保留原始错误信息
            throw e;
        } catch (Exception e) {
            // 处理其他类型的异常
            handleValidationException(e, transactionContext);
        }
    }
    
    /**
     * 设置所有监听器为校验模式
     */
    private void setupValidationMode(List<ReadSheet> readSheets, EasyExcelUtil.TransactionContext transactionContext) {
        for (ReadSheet sheet : readSheets) {
            ReadListener<?> listener = sheet.getCustomReadListenerList().get(0);
            if (listener instanceof AbstractBusinessListener<?> businessListener) {
                businessListener.setTransactionContext(transactionContext);
                businessListener.setValidationMode(true); // 设置为校验模式
                log.info("已为Sheet[{}:{}]设置校验模式", sheet.getSheetNo(), sheet.getSheetName());
            }
        }
    }
    
    /**
     * 校验每个sheet的数据
     */
    private void validateEachSheet(ExcelReader excelReader, List<ReadSheet> readSheets, 
                              EasyExcelUtil.TransactionContext transactionContext) {
        for (ReadSheet sheet : readSheets) {
            String sheetName = getSheetName(sheet);
            log.info("校验Sheet[{}:{}]", sheet.getSheetNo() + 1, sheetName);
            
            try {
                excelReader.read(Collections.singletonList(sheet));
            } catch (Exception e) {
                handleSheetValidationException(e, sheet, sheetName, transactionContext);
            }
            
            checkTransactionFailure(sheet, sheetName, transactionContext);
        }
    }
    
    /**
     * 获取格式化的sheet名称
     */
    private String getSheetName(ReadSheet sheet) {
        return sheet.getSheetName() != null ? 
            sheet.getSheetName() : "Sheet" + (sheet.getSheetNo() + 1);
    }
    
    /**
     * 处理单个sheet校验异常
     */
    private void handleSheetValidationException(Exception e, ReadSheet sheet, String sheetName, 
                                           EasyExcelUtil.TransactionContext transactionContext) {
        // 使用ExceptionUtils处理异常
        ExceptionUtils.ExceptionHandlingResult result = 
            ExceptionUtils.handleSheetException(e, sheet.getSheetNo() + 1, sheetName, "校验");
        
        if (result.shouldThrow()) {
            // 如果是业务异常或验证异常，直接抛出
            if (result.getExceptionType() == ExceptionUtils.ExceptionType.BUSINESS || 
                result.getExceptionType() == ExceptionUtils.ExceptionType.VALIDATION) {
                throw result.getAsRuntimeException();
            } else {
                // 处理其他异常
                transactionContext.setFailed(true);
                transactionContext.setErrorMessage(result.getErrorMessage());
                
                // 校验阶段检测到错误，抛出异常中断处理
                throw (TransactionProcessingException) result.getException();
            }
        }
    }
    
    /**
     * 检查事务是否已标记为失败
     */
    private void checkTransactionFailure(ReadSheet sheet, String sheetName, 
                                    EasyExcelUtil.TransactionContext transactionContext) {
        if (transactionContext.isFailed()) {
            String errorMsg = transactionContext.getErrorMessage();
            log.error("Sheet[{}:{}]校验未通过: {}", 
                     sheet.getSheetNo() + 1, sheetName, errorMsg);
            throw new TransactionProcessingException(errorMsg);
        }
    }
    
    /**
     * 处理校验阶段异常
     */
    private void handleValidationException(Exception e, EasyExcelUtil.TransactionContext transactionContext) {
        // 使用ExceptionUtils处理异常
        ExceptionUtils.ExceptionHandlingResult result = 
            ExceptionUtils.handleExcelProcessingException(e, "数据校验");
        
        if (result.shouldThrow()) {
            // 如果是业务异常或验证异常，直接抛出
            if (result.getExceptionType() == ExceptionUtils.ExceptionType.BUSINESS || 
                result.getExceptionType() == ExceptionUtils.ExceptionType.VALIDATION) {
                throw result.getAsRuntimeException();
            } else {
                // 处理其他异常
                transactionContext.setFailed(true);
                transactionContext.setErrorMessage(result.getErrorMessage());
                
                throw (TransactionProcessingException) result.getException();
            }
        }
    }
    
    /**
     * 处理所有sheet
     * 
     * @param fileBytes 文件字节数组
     * @param readSheets 读取sheet配置
     * @param transactionContext 事务上下文
     * @throws TransactionProcessingException 如果处理失败
     */
    private void processAllSheets(byte[] fileBytes, List<ReadSheet> readSheets, 
                              EasyExcelUtil.TransactionContext transactionContext) {
        log.info("开始处理所有sheet的数据");
        
        // 首先检查事务上下文是否已经标记为失败
        if (transactionContext.isFailed()) {
            log.error("校验阶段已发现错误，跳过保存阶段: {}", transactionContext.getErrorMessage());
            throw new TransactionProcessingException(transactionContext.getErrorMessage());
        }
        
        try (ExcelReader excelReader = createExcelReader(fileBytes)) {
            // 设置所有监听器的事务上下文为保存模式
            setupProcessingMode(readSheets, transactionContext);
            
            // 处理每个sheet
            processEachSheet(excelReader, readSheets, transactionContext);
            
            // 最终检查事务状态
            checkFinalTransactionStatus(transactionContext);
        }
    }
    
    /**
     * 设置监听器为保存模式
     */
    private void setupProcessingMode(List<ReadSheet> readSheets, EasyExcelUtil.TransactionContext transactionContext) {
        for (ReadSheet sheet : readSheets) {
            ReadListener<?> listener = sheet.getCustomReadListenerList().get(0);
            if (listener instanceof AbstractBusinessListener<?> businessListener) {
                businessListener.setTransactionContext(transactionContext);
                businessListener.setValidationMode(false); // 关闭校验模式，执行保存
                log.info("已为Sheet[{}:{}]设置事务上下文(保存模式)", sheet.getSheetNo(), sheet.getSheetName());
            }
        }
    }
    
    /**
     * 处理每个sheet的数据
     */
    private void processEachSheet(ExcelReader excelReader, List<ReadSheet> readSheets, 
                              EasyExcelUtil.TransactionContext transactionContext) {
        for (ReadSheet sheet : readSheets) {
            // 如果事务已标记为失败，跳过后续处理
            if (transactionContext.isFailed()) {
                log.warn("事务已标记为失败，跳过Sheet[{}]后续处理", sheet.getSheetNo() + 1);
                continue;
            }
            
            // 处理单个sheet
            processSheet(excelReader, sheet, transactionContext);
        }
    }
    
    /**
     * 处理单个sheet
     */
    private void processSheet(ExcelReader excelReader, ReadSheet sheet, 
                           EasyExcelUtil.TransactionContext transactionContext) {
        log.info("事务内处理sheet {}", sheet.getSheetNo() + 1);
        
        try {
            // 处理当前sheet
            excelReader.read(Collections.singletonList(sheet));
        } catch (Exception e) {
            // 获取sheet名称
            String sheetName = getSheetName(sheet);
            
            // 处理异常
            handleProcessingException(e, sheet, sheetName, transactionContext);
        }
    }
    
    /**
     * 处理sheet处理时的异常
     */
    private void handleProcessingException(Exception e, ReadSheet sheet, String sheetName,
                                      EasyExcelUtil.TransactionContext transactionContext) {
        // 使用ExceptionUtils处理异常
        ExceptionUtils.ExceptionHandlingResult result = 
            ExceptionUtils.handleSheetException(e, sheet.getSheetNo() + 1, sheetName, "处理");
        
        if (result.shouldThrow()) {
            // 如果是业务异常或验证异常，直接抛出
            if (result.getExceptionType() == ExceptionUtils.ExceptionType.BUSINESS || 
                result.getExceptionType() == ExceptionUtils.ExceptionType.VALIDATION) {
                throw result.getAsRuntimeException();
            } else {
                // 处理其他异常
                transactionContext.setFailed(true);
                transactionContext.setErrorMessage(result.getErrorMessage());
                
                // 抛出异常，触发后续所有处理的取消并确保事务回滚
                throw (TransactionProcessingException) result.getException();
            }
        }
    }
    
    /**
     * 检查最终的事务状态
     */
    private void checkFinalTransactionStatus(EasyExcelUtil.TransactionContext transactionContext) {
        if (transactionContext.isFailed()) {
            log.error("事务标记为失败，触发回滚: {}", transactionContext.getErrorMessage());
            throw new TransactionProcessingException(transactionContext.getErrorMessage());
        }
    }
    
    /**
     * 创建Excel读取器
     *
     * @param fileBytes 文件字节数组
     * @return Excel读取器实例
     */
    private ExcelReader createExcelReader(byte[] fileBytes) {
        return EasyExcelFactory.read(new ByteArrayInputStream(fileBytes))
                .registerConverter(new CustomBigDecimalConverter())
                .registerConverter(new CustomLocalDateConverter())
                .registerConverter(new CustomLocalDateTimeConverter())
                .registerConverter(new CustomDateConverter())
                .registerConverter(new CustomNumberConverter())
                .build();
    }
} 